/* LottoLuck.cpp
 * Author: mtreece
 * Date: 03/02/2010
 * Purpose:
 *  Main portal into the LottoLuck application.
 *  See the brief.txt for more information on how
 *  the application in entirety is to be described.
 * 
 *  Specifically, this will act as a main-interface/
 *  launcher to the rest of the software.
 *
 *  Note: This is the command-line interface only.
 *        If I ever get around to designing a GUI
 *        interface, it will be constructed elsewhere.
 */
#include "LottoLuck.h"

/* Fire the main engines... */
int main(int argc, char *argv[]){
	int errorCode = EXIT_OKAY;
	Environment env = {argc, argv};
	PassedArgs argdata;

	/* Obtain a definitions file for misc
	 * purposes: I'll need this to later
	 * statically set TicketLine's access
	 * to it: utmost needed for generation
	 * of random data and Ticket-checking
	 * for their values...
	 * We won't set it JUST yet; wait and
	 * make sure we'll need it after the
	 * arg checking and switching.
	 */
	Definitions def;

	/* Handle parsing of any arguments sent to
	 * my program; return a representation of
	 * how to continue from here.
	 */
	ProgramControl control = 
	   parseArgs(&env, &errorCode, &argdata);


	switch(control){
		case ERROR:
			/* Return error code. */
			errorCode = EXIT_ERROR;
			puts("ERROR...\n");
			puts(argdata.errmsg);
			break;
		case HELP:
			help();
			break;
		case ABOUT:
			about();
			break;
		case INTERACTIVE:
			puts("INTERACTIVE Mode...\n");
			routine_tasks(&def);
			interactive(&argdata, &def);
			break;
		case SINGLE_WINNER:
			puts("SINGLE_WINNER Mode...\n");
			routine_tasks(&def);
			single_winner(&argdata, &def);
			break;
		case RANDOM_TICKETS:
			puts("RANDOM_TICKETS Mode...\n");
			routine_tasks(&def);
			random_tickets(&argdata, &def);
			break;
		case DEFINED_TICKET:
			puts("DEFINED_TICKET Mode...\n");
			routine_tasks(&def);
			defined_ticket(&argdata, &def);
			break;
		default:
			/* this ever possible? */
			printf("An unknown error has occured. Sorry! :(\n");
			errorCode = EXIT_ERROR;
			break;
	}

	printf("Program terminated. Goodbye!\n");
	
	return errorCode;
}

ProgramControl parseArgs(Environment* env, int* errorCode, 
                         PassedArgs* argdata){
	ProgramControl out;
	int index = 0;

	/** Gather the mode of opartion... **/
	if(!hasArg(env, "--mode=", &index)){
		/* default... */
		out = INTERACTIVE;
	}else{
		char *cp = env->argv[index] + 7;
		strcpy(argdata->mode, cp);
		if(strcmp(cp, "INTERACTIVE") == 0){
			out = INTERACTIVE;
			/* No other information necessary,
			   since everything will be prompted. */
		}else if(strcmp(cp, "SINGLE_WINNER") == 0){
			out = SINGLE_WINNER;
			if(!hasArg(env, "--numtrials=", &index)){
				char err[MAXBUFF] = 
				   "Mode operation \""
				   "SINGLE_WINNER"
				   "\" requires --numtrials argument.\n";
				strcpy(argdata->errmsg, err);
				out = ERROR;
			}else{
				strcpy(argdata->numTrials, env->argv[index] + 12);
				if(atol(argdata->numTrials)<1){
					char err[MAXBUFF] =
					   "numtrials argument must" \
					   " be greater than or equal" \
					   " to one.\n";
					strcpy(argdata->errmsg, err);
					out = ERROR;
				}
			}
		}else if(strcmp(cp, "RANDOM_TICKETS") == 0){
			out = RANDOM_TICKETS;
			if(!hasArg(env, "--numtrials=", &index)){
				char err[MAXBUFF] = 
				   "Mode operation \""
				   "RANDOM_TICKETS"
				   "\" requires --numtrials argument.\n";
				strcpy(argdata->errmsg, err);
				out = ERROR;
			}else{
				strcpy(argdata->numTrials, env->argv[index] + 12);
				if(atol(argdata->numTrials)<1){
					char err[MAXBUFF] =
					   "numtrials argument must" \
					   " be greater than or equal" \
					   " to one.\n";
					strcpy(argdata->errmsg, err);
					out = ERROR;
				}
			}
			if(!hasArg(env, "--cpt=", &index)){
				char err[MAXBUFF] = 
				   "Mode operation \""
				   "RANDOM_TICKETS"
				   "\" requires --cpt argument.\n";
				strcpy(argdata->errmsg, err);
				out = ERROR;
			}else{
				strcpy(argdata->cpt, env->argv[index] + 6);
			}
		}else if(strcmp(cp, "DEFINED_TICKET") == 0){
			out = DEFINED_TICKET;
			if(!hasArg(env, "--numtrials=", &index)){
				char err[MAXBUFF] = 
				   "Mode operation \""
				   "DEFINED_TICKET"
				   "\" requires --numtrials argument.\n";
				strcpy(argdata->errmsg, err);
				out = ERROR;
			}else{
				strcpy(argdata->numTrials, env->argv[index] + 12);
				if(atol(argdata->numTrials)<1){
					char err[MAXBUFF] =
					   "numtrials argument must" \
					   " be greater than or equal" \
					   " to one.\n";
					strcpy(argdata->errmsg, err);
					out = ERROR;
				}
			}
			if(!hasArg(env, "--defsrc=", &index)){
				char err[MAXBUFF] = 
				   "Mode operation \""
				   "DEFINED_TICKET"
				   "\" requires --defsrc argument.\n";
				strcpy(argdata->errmsg, err);
				out = ERROR;
			}else{
				strcpy(argdata->defsrc, env->argv[index] + 9);
			}
		}else{
			char err[MAXBUFF] = "Mode type, \"";
			strcpy(err+12, cp);
			strcpy(err+12+strlen(cp), 
			    "\", not known. Try 'LottoLuck --help'"
			    " for proper syntax.\n");
			strcpy(argdata->errmsg, err);
			out = ERROR;
		}
	}
	/** /mode of operation**/

	/** Grab misc settings**/
	if(hasArg(env, "--verbose", &index)) argdata->verbose = 1;
	else argdata->verbose = 0;

	if(hasArg(env, "--config=", &index)) 
		strcpy(argdata->confFile, env->argv[index] + 9);
	else argdata->confFile[0] = '\0';
	/** /misc settings **/

	if(hasArg(env, "--help", &index)){
		/** --help option overrules all else. **/
		out = HELP;
		argdata->help = 1;
		*errorCode = 0;
	}else if(hasArg(env, "--about", &index)){
		/** --about option is next-in-line to
		 **   ruling out all else.            **/
		out = ABOUT;
		argdata->about = 1;
		*errorCode = 0;
	}
	return out;
}

int hasArg(Environment* env, const char* name, int* index){
	int env_len, name_len = strlen(name);
	for(int i=0; i<env->argc; i++){
		env_len = strlen(env->argv[i]);

		/* If our argument (env) is greater length than
		 * the name we're checking, BUT doesn't have
		 * the "=" at the name[]-length's place, then
		 * it's not what we're looking for, thus continue.
		 */
		if(env_len > name_len 
		   && env->argv[i][name_len-1] != '=') 
			continue;

		if(memcmp(env->argv[i], name, name_len) == 0){
			/* Save this index for future
			 * processing...
			 */
			*index = i;
			return 1;
		}
	}
	return 0;
}

void writeWebster(Definitions* def){
/******** Important values in config file...
 * MIN_ACCEPT_TICKET_NUM  # min value allowed for normal num
 * MAX_ACCEPT_TICKET_NUM  # max " " " " "
 * MIN_ACCEPT_PB_NUM      # min value allowed for pb num
 * MAX_ACCEPT_PB_NUM      # max " " " " "
 * TICKET_COST            # cost per ticket-set: usually $1
 * VERBOSE_OUTPUT         # 1 = true, 0 = false; written to stderr
 * P0_0        # prize value for 0 nums match + 0 pb match
 * P0_1        # prize value for 0 nums match + 1 pb match
 * P1_0        # prize value for 1 nums match + 0 pb match
 * P1_1        # prize value for 1 nums match + 1 pb match
 * P2_0        # prize value for 2 nums match + 0 pb match
 * P2_1        # prize value for 2 nums match + 1 pb match
 * P3_0        # prize value for 3 nums match + 0 pb match
 * P3_1        # prize value for 3 nums match + 1 pb match
 * P4_0        # prize value for 4 nums match + 0 pb match
 * P4_1        # prize value for 4 nums match + 1 pb match
 * P5_0        # prize value for 5 nums match + 0 pb match
 * P5_1        # prize value for 5 nums match + 1 pb match
 */
	ConfReader *r = new ConfReader(CONF_FILE_NAME);
	if( !r->isReady() ){
		fprintf(stderr, "Cannot find '%s'; generating and using it...\n", CONF_FILE_NAME);
		generateConfFile(CONF_FILE_NAME);
		r = new ConfReader(CONF_FILE_NAME);
	}
	char need[][30] = { "MIN_ACCEPT_TICKET_NUM", "MAX_ACCEPT_TICKET_NUM",
	                    "MIN_ACCEPT_PB_NUM",     "MAX_ACCEPT_PB_NUM",
	                    "TICKET_COST",           "VERBOSE_OUTPUT",
	                    "P0_0", "P0_1", "P1_0", "P1_1", "P2_0", "P2_1",
	                    "P3_0", "P3_1", "P4_0", "P4_1", "P5_0", "P5_1",
                           };
	unsigned int put[4];
	unsigned int ticketCost;
	int verbose;
	unsigned int worths[6][2];
	for(int i=0; i<4; i++){
		put[i] = atoi(
		               r->getValue(need[i])
		             );
	}
	ticketCost = atoi(
	                   r->getValue(need[4])
	                 );
	verbose = atoi(
	               r->getValue(need[5])
	              );
	for(int i=0; i<6; i++){
		worths[i][0] = atoi(
		                     r->getValue(need[6 + 2*i]) 
		                   );
		worths[i][1] = atoi( 
		                     r->getValue(need[6 + 2*i + 1]) 
		                   );
	}
	Definitions* builder = 
	   new Definitions(put[0], put[1], put[2], put[3], ticketCost, verbose, worths);
	memcpy(def, builder, sizeof(Definitions));
	delete(builder);
}

void generateConfFile(const char * fileName){
	ofstream file(fileName, ios::out);
	char date[100];
	getTimestamp(date);
	file << DEFAULT_CONFIGURATION_FILE;
	file << date << ".\n";
	file << "## End of configuration file.\n";
	file.close();
}

void getTimestamp(char*output){
	/* Code modified from: http://www.daniweb.com/forums/thread77400.html */
	struct tm *sys_t = NULL;
	time_t tval = 0;
	tval = time(NULL);
	sys_t = localtime(&tval);
	int day=sys_t->tm_mday;
	int month=sys_t->tm_mon+1;
	int year=1900 + sys_t->tm_year;
	// printf("\nMonth = %d", Month);
	// printf("\nDay = %6.6f", Day);
	// printf("\nYear = %d", Year);
	sprintf(output, "%d/%d/%d", month, day, year);
}

void help(void){
	puts(HELP_MESSAGE);
}

void about(void){
	puts(ABOUT_MESSAGE);
}

void routine_tasks(Definitions* def){
	writeWebster(def);

	/* Install Webster-dictionary... */
	TicketLine::setDefinitions(def);
}

void interactive(PassedArgs* args, Definitions* def){
	const char error[] =
	  "  ERROR!  This mode has not yet been implemented.\n"
	  "  Please try the --help argument for more information about the program,\n"
	  "  including other modes of operation.\n";
	puts(error);

	/* Make the compiler quit warning... */
	args = NULL;
	def = NULL;
}

void single_winner(PassedArgs* args, Definitions* def){
	/******************************************
	   Review of what's to be done.......     *
	   Here, this mode has a SINGLE winning   *
	   ticket, which is randomly generated.   *
	   Then, based off of --numtrials, we'll  *
	   generate --numtrials amount of tickets *
	   and count our winnings.                *
	                                          *
	   Primary concern: how to handle the     *
	   results??  I have a verbose-flag I can *
	   distinguish results; but what kind of  *
	   output should be given on this run?    *
	     1) List ALL tickets generated,       *
	        including its worth               *
	     2) List only the tickets worth       *
	        anything, including worth         *
	     3) Only show the collective          *
	        statistics at the end.            *
	                                          *
	   My decided approach...                 *
	     1) no --verbose = only print stats,  *
	        as, I'll probably do a LOT of     *
	        super-large numtrials for         *
	        statistical analysis.             *
	     2) --verbose = print ONLY the        *
	        tickets that are worth anything.  *
	        The disadvantage to this is lost  *
	        information, but if anyone wants  *
	        to see random tickets generated   *
	        they can always use my            *
	        TicketGenerator subproject.       *
	*******************************************/
	long int numtrials = 
	    atol( args->numTrials );
	unsigned int winnings = 0;
	unsigned int matches = 0;
	unsigned int jpmatches = 0;
	unsigned int max_won = 0;
	unsigned int num_max_won = 1;

	// TODO: Make everything long / unsigned
	//       so as to allow for super-large
	//       simulations... 
	//       also consider doing a quick 'print'
	//       of the result to make sure it's 
	//       interpreted properly...

	TicketLine *winner, *current;
	winner = new TicketLine();
	MoneyWorth* mw;
	fprintf(stdout, "numtrials = %lu\n\n", numtrials);
	if(args->verbose) fprintf(stderr, "==== Matching Tickets... =================\n");
	for(long int i=0; i<numtrials; i++){
		current = new TicketLine();
		mw = current->getWorth(winner);
		if(mw->value() > 0){
			if(args->verbose){
				fprintf(stderr, "   | %s| = $%d\n", 
				     current->toString(), mw->value());
			}
			matches++;
			if(mw->value() > max_won) {
				max_won = mw->value();
				num_max_won = 1;
			}
			else if(mw->value() == max_won){
				num_max_won++;
			}
		}
		if(mw->jackpot()){
			jpmatches++;
		}
		winnings += mw->value();

		/* Without these delete()'s, memory leakage is insane
		 * for larger statistical trials... */
		delete(current);
		delete(mw);
	}
	if(args->verbose) fprintf(stderr, "==========================================\n\n");
	int spent = def->ticketPrice() * numtrials;
	int net = winnings - spent;
	puts("Summary:\n"
	     "===============");
	printf("Winning Ticket... | %s|\n", winner->toString());
	printf("Ticket Matches...  %d\n", matches);
	printf("Jackpot Matches..  %d\n", jpmatches);
	printf("Cost / Ticket.... $%d\n", def->ticketPrice());
	printf("Max Ticket Win... $%d (x%d)\n", max_won, num_max_won);
	printf("Money Spent...... $%d\n", spent);
	printf("Money Earned..... $%d\n", winnings);
	printf("Net Earnings..... $%d\n", net);
	printf("%% Returns........ %f%%\n", 100* ((double) winnings) / ((double) spent));

	printf("\n");
}

void random_tickets(PassedArgs* args, Definitions* def){
	// TODO: Modify this for this function!
	/******************************************
	   Review of what's to be done.......     *
	   Here, we'll need to access the stored  *
	   cpt value, then for numtrials, we'll   *
	   generate...                            *
	      1   winning ticket (random)         *
	      cpt TicketLines    (random)         *
	   ... and during the creation of each    *
	   TicketLine, we'll check it's value and *
	   run the standard algorithms (de above) *
	   (and continue from there...).          *
	   
	*******************************************/
	long int numtrials = 
	    atol( args->numTrials );
	long int cpt =
	    atol( args->cpt );
	unsigned int winnings = 0;
	unsigned int matches = 0;
	unsigned int jpmatches = 0;
	unsigned int max_won = 0;
	unsigned int num_max_won = 1;

	// TODO: Make everything long / unsigned
	//       so as to allow for super-large
	//       simulations... 
	//       also consider doing a quick 'print'
	//       of the result to make sure it's 
	//       interpreted properly...

	TicketLine* winner;
	Ticket* current;
	MoneyWorth* mw;
	fprintf(stdout, "numtrials = %lu\n", numtrials);
	fprintf(stdout, "cpt       = %lu\n\n", cpt);
	if(args->verbose) fprintf(stdout, "==== Matching Tickets... =================\n");
	for(long int i=0; i<numtrials; i++){
		current = new Ticket(cpt);
		current->addAllRandom();
		winner = new TicketLine();
		mw = current->getWorth(winner);
		if(mw->value() > 0){
			if(args->verbose){
				current->printTicket();
				printf("Winner... | %s|\n", winner->toString());
				printf("... worth-> $%d\n\n", mw->value());
			}
			matches++;
			if(mw->value() > max_won){
				max_won = mw->value();
				num_max_won = 1;
			}
			else if(mw->value() == max_won){
				num_max_won++;
			}
		}
		if(mw->jackpot()){
			/* On the super-rare occasion
			 * 2+ jackpots occur on the same
			 * Ticket, this will handle that.
			 */
			jpmatches += mw->jackpot();
		}
		winnings += mw->value();

		/* Without these delete()'s, memory leakage is insane
		 * for larger statistical trials... */
		delete(current);
		delete(mw);
		delete(winner);
	}
	// TODO: Synchronize printing Tickets with the proper std[w/e]
	if(args->verbose) fprintf(stdout, "==========================================\n\n");
	int spent = def->ticketPrice() * numtrials * cpt;
	int net = winnings - spent;
	puts("Summary:\n"
	     "===============");
	printf("Ticket Matches...  %d\n", matches);
	printf("Jackpot Matches..  %d\n", jpmatches);
	printf("Cost / Ticket.... $%lu\n", cpt * def->ticketPrice());
	printf("Max Ticket Win... $%d (x%d)\n", max_won, num_max_won);
	printf("Money Spent...... $%d\n", spent);
	printf("Money Earned..... $%d\n", winnings);
	printf("Net Earnings..... $%d\n", net);
	printf("%% Returns........ %f%%\n", 100* ((double) winnings) / ((double) spent));

	printf("\n");
}

void defined_ticket(PassedArgs* args, Definitions* def){
	/******************************************
	   Review of what's to be done.......     *
	   Here, we're dealing with a constant-   *
	   defined ticket that the user is        *
	   handing to us...                       *
	   They will pass us this data via a      *
	   command-line argument --defsrc='X',    *
	   where X can either be '-' or a         *
	   file-name...                           *
	                                          *
	   If defsrc='-', then the tickets will   *
	   be entered via the console, and when   *
	   finished, will hit EOF (if using a     *
	   keyboard to type this, ^D simulates    *
	   this in Linux, ^Z in Windows...        *
	                                          *
	   Elsewise, defsrc should be a valid     *
	   filename; so, we do a check on this    *
	   filename, ensure it exists and we can  *
	   read it, then process it...            *
	                                          *
	   A valid format for this input will be  *
	   like so...                             *
	                                          *
                        N N N N N P               *
	                N N N N N P               *
	                ...                       *
	                N N N N N P               *
	                                          *
	   (i.e., whitespace delim'td 5 numbers + *
	    1 powerball at the end + newline... ) *
	                                          *
	   Read these numbers into a LinkedList   *
	   data structure as they are read,       *
	   incr'ing a counter (for later ease),   *
	   and then when done reading, create a   *
	   new Ticket off of the size read in and *
	   add each entry of the LinkedList.      *
	                                          *
	   Then, all that is left is to run it    *
	   through the typical routine of the     *
	   Lottery and display results :-).       *
	*******************************************/

	// Grab the ticket...
	Ticket* ticket = getSrcTicket(args->defsrc);
	if(ticket == NULL){
		// in this case, error occurred...
		fprintf(stderr, "Error reading from source...\n");
		fprintf(stderr, "... aborting mission!\n");
		fprintf(stderr, "(please check source and source's format.)\n\n");
		return;
	}

	// elsewise, let's do this!

	long int numtrials = 
	    atol( args->numTrials );
	unsigned int winnings = 0;
	unsigned int matches = 0;
	unsigned int jpmatches = 0;
	unsigned int max_won = 0;
	unsigned int num_max_won = 1;

	// TODO: Make everything long / unsigned
	//       so as to allow for super-large
	//       simulations... 
	//       also consider doing a quick 'print'
	//       of the result to make sure it's 
	//       interpreted properly...

	TicketLine *winner;
	MoneyWorth* mw;
	fprintf(stdout, "numtrials = %lu\n\n", numtrials);
	fprintf(stdout, "Using the ticket...\n");

	// pretty-print the ticket
	TicketLine* current;
	puts("=== Ticket           ============");
	for(int i=0; i<ticket->size(); i++){
		current = ticket->getTicket(i);
		printf("   | %s|\n", current->toString());
	}
	puts("=================================\n");

	if(args->verbose) fprintf(stderr, "==== Matching Winners... =================\n");
	for(long int i=0; i<numtrials; i++){
		winner = new TicketLine();
		mw = ticket->getWorth(winner);
		if(mw->value() > 0){
			if(args->verbose){
				fprintf(stderr, "   | %s| = $%d\n", 
				     winner->toString(), mw->value());
			}
			matches++;
			if(mw->value() > max_won) {
				max_won = mw->value();
				num_max_won = 1;
			}
			else if(mw->value() == max_won){
				num_max_won++;
			}
		}
		if(mw->jackpot()){
			jpmatches++;
		}
		winnings += mw->value();

		/* Without these delete()'s, memory leakage is insane
		 * for larger statistical trials... */
		delete(winner);
		delete(mw);
	}
	if(args->verbose) fprintf(stderr, "==========================================\n\n");
	int spent = def->ticketPrice() * numtrials * ticket->size();
	int net = winnings - spent;
	puts("Summary:\n"
	     "===============");
	printf("Ticket Matches...  %d\n", matches);
	printf("Jackpot Matches..  %d\n", jpmatches);
	printf("Cost / Ticket.... $%d\n", def->ticketPrice() * ticket->size());
	printf("Max Ticket Win... $%d (x%d)\n", max_won, num_max_won);
	printf("Money Spent...... $%d\n", spent);
	printf("Money Earned..... $%d\n", winnings);
	printf("Net Earnings..... $%d\n", net);
	printf("%% Returns........ %f%%\n", 100* ((double) winnings) / ((double) spent));

	printf("\n");
}

/* This function accepts a string (representing the
 * source), and returns the address of a Ticket in 
 * memory... the source can either be "-", indicative
 * that the user is to input the ticket via STDIN or
 * may be non-"-", indicative that the source should be
 * interpreted as a file-name - upon which this file is
 * opened, tokenized, Ticketified, then closed. Subsequently
 * the Ticket address is returned for further processing.
 */
Ticket* getSrcTicket(char* src){
	list<TicketLine*> lines;

	/* If the src = "-", use stdin; elsewise, use
	 * something from the filesystem...
	 */
	FILE* source = (strcmp(src, "-")==0 ? stdin : 
	                fopen(src, "r"));

	/* If our source is NULL, it means fopen 
	 * couldn't open file; return NULL!
	 */
	if(source == NULL) return NULL;

	int nums[5], pb;
	while(fscanf(source, "%d %d %d %d %d %d\n", 
	             nums + 0, nums + 1, nums + 2, 
	             nums + 3, nums + 4, &pb) == 6){
		lines.push_back(
		               new TicketLine(nums[0], nums[1],
		                              nums[2], nums[3],
		                              nums[4], pb)
		               );
	}

	// If our source wasn't stdin, (ie was a file), close it.
	if(strcmp(src, "-") != 0) fclose(source);

	Ticket* out = new Ticket(lines.size());
	list<TicketLine*>::iterator it;
	for(it = lines.begin(); it != lines.end(); it++){
		out->addTicketLine(*it);
	}

	return out;
}
